(ns samber.services.user.login
  (:require
    [toucan.db :as db]
    [honeysql.core :as hsql]
    [samber.db.models :as models]
    [java-time :as time]
    [clojure.tools.logging :as log]
    [samber.config :refer [env]]
    [promesa.exec :as exec]
    [cuerdas.core :as str]
    [samber.middleware.exception :as exception]
    [buddy.hashers :as hashers]
    [buddy.sign.jwt :as jwt]))


(defn- do-login [user addr]
  "user & remote-addr"
  (let [{:keys [username password]} user
        entity (first (db/select models/User {:where [:or [:= :username username] [:= :mobile username] [:= :email username]]}))]

    ;; check if user not exits
    (if (empty? entity)
      (throw (ex-info "check" {:type ::exception/check :msg "user not exists!"})))

    ;; check locked
    (if (:locked_at entity)
      (throw (ex-info "check" {:type ::exception/check :msg "locked!"})))

    ;; check password
    (if-not (hashers/check password (:encrypted_password entity))
      (let [attempt-params {:updated_at       (hsql/raw "now()")
                            :failed_attempts  (hsql/call :+ :failed_attempts 1)}
            attempts (:failed_attempts entity)
            locked-params (if attempts (> attempts 3)
                            {:locked_at        (hsql/raw "now()")})]
        (exec/schedule! 10
          #(db/update! models/User (:id entity) (merge attempt-params locked-params)))

        (throw (ex-info "check" {:type ::exception/check :msg "password not match!"}))))

    ;; check ended ..................

    ;; update statistics record
    (let [up-params {:sign_in_count           (hsql/call :+ :sign_in_count 1)
                     :current_sign_in_at      (hsql/raw "now()")
                     :last_sign_in_ip         (hsql/call := :current_sign_in_ip)
                     :current_sign_in_ip      addr
                     :updated_at              (hsql/raw "now()")
                     :failed_attempts         0}]
      (exec/schedule! 10
        #(db/update! models/User (:id entity) up-params)))

    ;; generate token
    (let [uuid (clojure.string/replace (.toString (java.util.UUID/randomUUID)) #"-" "")
          exp (-> (time/plus (time/zoned-date-time) (time/days 90))
                  time/instant
                  time/to-millis-from-epoch)
          claims {:id (:id entity)
                  :jti uuid
                  :exp exp}
          token (jwt/sign claims "11myvery--mmmm" {:alg :hs512})]


      {:code 0 :token token :msg "success"})))


(defn login [user addr]
  (do-login user addr))
  ;; some other checks
  ; (do-check2 user)
